#include "Quadruples.h"
#include "TO_AST.h"
#include <stdlib.h>

int symbolTableIdx = 0;
const string success = "success";

static int labelCnt = 0;

static string nextLabel() {
    return "label_" + std::to_string(labelCnt++);
}

string concatTheT();
void Quadruple_Add(list<Quadruple*>& Q, string dest, string op1, Operator op, string op2);
TYPE GET_TYPE(string type);
SymRecordType* GET_RECORD_TYPE(const Type* type, SymbolTable* now);
list<Quadruple*> GET_PPROGRAM(const Program* program);
void GET_program_body(const Program_Body* program_body, SymbolTable* now, list<Quadruple*>& Q);
void GET_Const_Declaration(const Const_Declaration* const_declaration, SymbolTable* now);
PSI Const_Variable_To_Int(const Const_Variable* const_variable, SymbolTable* now);
void GET_Var_Declaration(const Var_Declaration* var_declaration, SymbolTable* now);
void GET_Subprogram_Declaration(const Subprogram_Declaration* subprogram_declaration, SymbolTable* now, list<Quadruple*>& Q, int number);
SymType* GET_Type_From_Type(const Type* type, SymbolTable* now);
void Int_To_Real(PTS& type_int, list<Quadruple*>& Q);
TYPE TYPE_CHECK(PTS& left, PTS& right, Operator op, list<Quadruple*>& Q);
TYPE TYPE_CHECK(PTS& Type, Operator op, list<Quadruple*>& Q);
void GET_Compound_Statement(const Compound_Statement* compound_statement, SymbolTable* now, list<Quadruple*>& Q);
void AUTO_GET_Statement(const Statement* pStatement, SymbolTable *now, list<Quadruple*> &Q);
void GET_Assign_Statement(const Assign_Statement* assign_statement, SymbolTable* now, list<Quadruple*>& Q);
void GET_Procedure_Statement(const Procedure_Statement* procedure_statement, SymbolTable* now, list<Quadruple*>& Q);
void GET_IF_Statement(const IF_Statement* if_statement, SymbolTable *now, list<Quadruple*> &Q);
void GET_CASE_Statement(const CASE_Statement* case_statement, SymbolTable* now, list<Quadruple*> &Q);
void GET_WHILE_Statement(const WHILE_Statement* while_statement, SymbolTable* now, list<Quadruple*> &Q) ;
void GET_REPEAT_Statement(const REPEAT_Statement *repeat_statement, SymbolTable *now, list<Quadruple*> &Q);
void GET_FOR_Statement(const FOR_Statement *for_statement, SymbolTable *now, list<Quadruple*> &Q);
PTS GET_Expression(const Expression* expression, SymbolTable* now, list<Quadruple*>& Q);
PTS GET_Simple_Expression(const Simple_Expression* simple_expression, SymbolTable* now, list<Quadruple*>& Q);
PTS GET_Term(const Term* term, SymbolTable* now, list<Quadruple*>& Q);
PTS GET_Factor(const Factor* factor, SymbolTable* now, list<Quadruple*>& Q);
string GET_Index(Sym_array* array, vector<string>& index_list, list<Quadruple*>& Q);
PTS GET_Variable(const Variable* variable, SymbolTable* now, list<Quadruple*>& Q, bool is_left, bool &is_address);

string concatTheT()
{
    symbolTableIdx++;
    return "t" + to_string(symbolTableIdx);
}

void Quadruple_Add(list<Quadruple*>& Q, string dest, string op1, Operator op, string op2)
{
    Quadruple* res = new Quadruple(dest, op1, op, op2);
    Q.push_back(res);
}

TYPE GET_TYPE(string type)
{
    TYPE ans = TYPE::NEXIST;
    if(type == "INTEGER" || type == "int" || type == "integer"){
        ans = TYPE::INT;
    }
    else if(type == "REAL" || type == "float" || type == "real"){
        ans = TYPE::FLOAT;
    }
    else if(type == "BOOLEAN" || type == "boolean"){
        ans = TYPE::BOOL;
    }
    else if(type == "CHAR" || type == "char"){
        ans = TYPE::CHAR;
    }
    else if(type == "STRING" || type == "string"){
        ans = TYPE::STRING;
    }
    return ans;
}

SymRecordType* GET_RECORD_TYPE(const Type* type, SymbolTable* now)
{
    vector<pair<string, SymType*>> record_type;
    for (auto &var: type->var_declaration) {
        for (auto &varID: var->ID) {
            // cout << "name: " << varID.id << "\n";
            record_type.push_back(make_pair(varID.id, GET_Type_From_Type(var->type, now)));
        }
    }
    // cout << "Got record ...\n";
    auto ret = new SymRecordType(record_type);
    // cout << "new a record\n";
    return ret;
}

list<Quadruple*> GET_PPROGRAM(const Program* program)
{
    list<Quadruple*> Quadruples;
    SymbolTable* root = new SymbolTable();
    root->insert_model_name(program->ID.id, program->ID.line);
    Quadruple* res = new Quadruple(program->ID.id, "", Operator::PROC, "");
    Quadruples.push_back(res);
    //parameter
    // cout << "To get program body ... \n";
    GET_program_body(program->program_body, root, Quadruples);
    return Quadruples;
}

void GET_program_body(const Program_Body* program_body, SymbolTable* now, list<Quadruple*>& Q)
{
    for(auto it = program_body->const_declaration.begin(); it != program_body->const_declaration.end(); it++){
        // cout << "To get const delcaration ... \n";
        GET_Const_Declaration((*it), now);
    }
    for(auto it = program_body->var_declaration.begin(); it != program_body->var_declaration.end(); it++){
        // cout << "To get var delcaration ... \n";
        GET_Var_Declaration((*it), now);
    }
    int number = 0;
    for(auto it = program_body->subprogram_declaration.begin(); it != program_body->subprogram_declaration.end(); it++){
        // cout << "To get subprogram delcarartion ...\n";
        GET_Subprogram_Declaration((*it), now, Q, number);
        number++;
    }   
    // cout << "To get subprogram declaration ... \n";
    GET_Compound_Statement(program_body->compound_statement, now, Q);
}

void GET_Const_Declaration(const Const_Declaration* const_declaration, SymbolTable* now)
{
    string left_id = const_declaration->ID.id;
    int line = const_declaration->ID.line;
    CLASS find_left = now->find_ID(left_id);
    bool in_head = now->rename_head(left_id);
    if(find_left == CLASS::NEXIST && (!in_head)){
        if(const_declaration->const_variable->mold == "id"){
            string right_id = const_declaration->const_variable->id;
            CLASS find_right = now->find_all_ID(right_id);
            if(find_right == CLASS::CONST){
                PTS res = now->find_ConstVariable_value(right_id);
                bool is_minus = const_declaration->const_variable->is_minus;
                now->insert_ConstVariable(left_id, line, res.first, is_minus, res.second);
            }
            else{
                cout << "error incorrect type" << endl;
            }
        }
        else{
            string type = const_declaration->const_variable->mold;
            TYPE Type = TYPE::NEXIST;
            if(type == "int"){
                Type = TYPE::INT;
            }
            else if(type == "float"){
                Type = TYPE::FLOAT;
            }
            else if(type == "char"){
                Type = TYPE::CHAR;
            }
            else if(type == "bool"){
                Type = TYPE::BOOL;
            }
            else if(type == "string"){
                Type = TYPE::STRING;
            }
            bool is_minus = const_declaration->const_variable->is_minus;
            string value = const_declaration->const_variable->var_value.id;
            now->insert_ConstVariable(left_id, line, Type, is_minus, value);
        }
    }
    else{
        cout << "error const name exist!" << endl;
    }
}

PSI Const_Variable_To_Int(const Const_Variable* const_variable, SymbolTable* now)
{
    if(const_variable->mold == "id"){
        CLASS find_id = now->find_all_ID(const_variable->id);
        if(find_id == CLASS::CONST){
            PTS res = now->find_ConstVariable_value(const_variable->id);
            if(res.first != TYPE::INT){
                PSI psi = make_pair("index type is not integer", 0);
                return psi;                      
            }
            else{
                PSI psi = make_pair(success, atoi(res.second.c_str()));
                return psi;
            }
        }
        else{
            PSI psi = make_pair("index type is not const", 0);
            return psi;           
        }
    }
    else if(const_variable->mold == "int"){
        PSI psi = make_pair(success, const_variable->int_value);
        return psi;
    }
    else{
        PSI psi = make_pair("index not int", 0);
        return psi;
    }
}

void GET_Var_Declaration(const Var_Declaration* var_declaration, SymbolTable* now)
{
    string Type_type = var_declaration->type->mold;
    // cout << "Var's type is " << Type_type << "\n";
    if(Type_type == "standard"){
        string standard = var_declaration->type->standard.id;
        TYPE var_type = GET_TYPE(standard);
        for(auto it = var_declaration->ID.begin(); it != var_declaration->ID.end(); it++){
            string left_id = (*it).id;
            int line = (*it).line;
            CLASS find_left = now->find_ID(left_id);
            bool in_head = now->rename_head(left_id); 
            if(find_left == CLASS::NEXIST && (!in_head)){
                now->insert_Variable(left_id, line, var_type);
            }
            else{
                cout << "error variable name exist!" << endl;
            }
        }
    }
    else if(Type_type == "array"){
        vector<PII> array_range;
        for(auto it = var_declaration->type->array_range.begin(); it != var_declaration->type->array_range.end(); it++){
            Const_Variable* range_left = (*it).first;
            Const_Variable* range_right = (*it).second;
            PSI get_left = Const_Variable_To_Int(range_left, now);
            PSI get_right = Const_Variable_To_Int(range_right, now);
            if(get_left.first == success && get_right.first == success){
                int left = get_left.second;
                int right = get_left.second;
                if(left > right){
                    cout << "Upper bound of range is less than lower bound" << endl;
                }
                else{
                    array_range.push_back(make_pair(left, right));
                }
            }
            else{
                if(get_left.first != success){
                    cout << get_left.first << endl;
                }
                if(get_right.first != success){
                    cout << get_right.first << endl;
                }                
            }
        }
        if(var_declaration->type->array_type == "standard"){
            string standard = var_declaration->type->standard.id;
            TYPE array_type = GET_TYPE(standard);
            for(auto it = var_declaration->ID.begin(); it != var_declaration->ID.end(); it++){
                string left_id = (*it).id;
                int line = (*it).line;
                CLASS find_left = now->find_ID(left_id);
                bool in_head = now->rename_head(left_id); 
                if(find_left == CLASS::NEXIST && (!in_head)){
                    now->insert_Array(left_id, line, array_type, array_range);
                }
                else{
                    cout << "error Array name exist!" << endl;
                }
            }
        }
        else{
            // SymRecordType* record_type = GET_RECORD_TYPE(var_declaration->type, now);
            // for(auto it = var_declaration->ID.begin(); it != var_declaration->ID.end(); it++){
            //     string left_id = (*it).id;
            //     int line = (*it).line;
            //     CLASS find_left = now->find_ID(left_id);
            //     bool in_head = now->rename_head(left_id); 
            //     if(find_left == CLASS::NEXIST && (!in_head)){
            //         now->insert_Array_Record(left_id, line, record_type, array_range);
            //     }
            //     else{
            //         cout << "error Array name exist!" << endl;
            //     }
            // }
        }
    }
    else if(Type_type == "record"){
        SymRecordType* record_type = GET_RECORD_TYPE(var_declaration->type, now);
        // cout << "Got record ...\n";
        for(auto it = var_declaration->ID.begin(); it != var_declaration->ID.end(); it++){
            string left_id = (*it).id;
            int line = (*it).line;
            CLASS find_left = now->find_ID(left_id);
            bool in_head = now->rename_head(left_id); 
            if(find_left == CLASS::NEXIST && (!in_head)){
                now->insert_record(left_id, line, record_type);
            }
            else{
                cout << "error record name exist!" << endl;
            }
        }
    }
}

void GET_Subprogram_Declaration(const Subprogram_Declaration* subprogram_declaration, SymbolTable* now, list<Quadruple*>& Q, int number)
{
    string mold = subprogram_declaration->mold;
    string left_id = subprogram_declaration->ID.id;
    int line = subprogram_declaration->ID.line;
    CLASS find_left = now->find_all_ID(left_id);
    bool rename_head = now->rename_head(left_id);
    if(find_left == CLASS::NEXIST && (!rename_head)){
        SymbolTable* sub_symboltable = new SymbolTable();
        sub_symboltable->preSymbolTable = now;
        sub_symboltable->insert_model_name(left_id, line);
        for(auto it1 = subprogram_declaration->parameter.begin(); it1 != subprogram_declaration->parameter.end(); it1++){
            string t = (*it1)->type.id;
            TYPE para_type = GET_TYPE(t);
            bool is_reference = (*it1)->is_reference;
            for(auto it2 = (*it1)->ID.begin(); it2 != (*it1)->ID.end(); it2++){
                string id = (*it2).id;
                int line = (*it2).line;
                bool is_rename = sub_symboltable->find_parameter(id);
                if(is_rename){
                    cout << "parameter renamed" << endl;
                }
                else{
                    sub_symboltable->insert_parameter(id, line, para_type, is_reference);
                }
            }
        }
        TYPE return_type = TYPE::NEXIST;
        int line = 0;
        if(mold == "function"){
            return_type = GET_TYPE(subprogram_declaration->return_type.id);
            line = subprogram_declaration->return_type.line;
        }
        sub_symboltable->insert_return_value(return_type, line);
        list<Quadruple*> sub_Q;
        Quadruple* first = nullptr;
        if(mold == "function"){
            Quadruple_Add(sub_Q, sub_symboltable->head, "", Operator::FUNC, "");
        }
        else{
            Quadruple_Add(sub_Q, sub_symboltable->head, "", Operator::PROC, "");
        }
        for(auto it = sub_symboltable->parameterList.begin(); it != sub_symboltable->parameterList.end(); it++){
            Quadruple_Add(sub_Q, (*it)->nameInComputer, "", Operator::PARAM, "");
        }
        GET_program_body(subprogram_declaration->program_body, sub_symboltable, sub_Q);
        if(mold == "function"){
            string return_name = sub_symboltable->find_return_inname();
            Quadruple_Add(sub_Q, return_name, "", Operator::RETURN, "");
        }
        Quadruple_Add(sub_Q, "", "", Operator::END, "");
        auto it = Q.begin();
        int num = 0;
        while(num < number && it != Q.end()){
            while((*it)->op != Operator::END){
                it++;
            }
            num += 1;
            it++;
        }
        Q.splice(it, sub_Q);

        if(mold == "function"){
            now->insert_function(sub_symboltable);
        }
        else if(mold == "procedure"){
            now->insert_procedure(sub_symboltable);
        }
    }
    else{
        cout << "error " << mold << "name exist!" << endl;
    }
}

SymType* GET_Type_From_Type(const Type* type, SymbolTable* now)
{
    if (type->mold == "standard") {
        string stdType = type->standard.id;
        if (stdType == "integer"){
            return new SymStdType(TYPE::INT);
        }
        else if (stdType == "real") {
            return new SymStdType(TYPE::FLOAT);
        }
        else if (stdType == "boolean") {
            return new SymStdType(TYPE::BOOL);
        }
        else if (stdType == "char") {
            return new SymStdType(TYPE::CHAR);
        }
        else if (stdType == "string") {
            return new SymStdType(TYPE::STRING);
        }
        else {
            return new SymStdType(TYPE::NEXIST);
        }
    }
    else if (type->mold == "array") {
        vector<pair<size_t, size_t>> array_range;
        for(auto it = type->array_range.begin(); it != type->array_range.end(); it++){
            Const_Variable* range_left = (*it).first;
            Const_Variable* range_right = (*it).second;
            PSI get_left = Const_Variable_To_Int(range_left, now);
            PSI get_right = Const_Variable_To_Int(range_right, now);
            if(get_left.first == success && get_right.first == success){
                size_t left = get_left.second;
                size_t right = get_left.second;
                if(left > right){
                    cout << "Upper bound of range is less than lower bound" << endl;
                }
                else{
                    array_range.push_back(make_pair(left, right));
                }
            }
            else{
                if(get_left.first != success){
                    cout << get_left.first << endl;
                }
                if(get_right.first != success){
                    cout << get_right.first << endl;
                }                
            }
        }
        if(type->array_type == "standard"){
            string standard = type->standard.id;
            TYPE array_enum_type = GET_TYPE(standard);
            switch (array_enum_type) {
                case TYPE::INT:
                    return new SymArrayType(new SymStdType(TYPE::INT), std::move(array_range));
                case TYPE::FLOAT:
                    return new SymArrayType(new SymStdType(TYPE::FLOAT), std::move(array_range));
                case TYPE::CHAR:
                    return new SymArrayType(new SymStdType(TYPE::CHAR), std::move(array_range));
                case TYPE::BOOL:
                    return new SymArrayType(new SymStdType(TYPE::BOOL), std::move(array_range));
                case TYPE::STRING:
                    return new SymArrayType(new SymStdType(TYPE::STRING), std::move(array_range));
                default:
                    return new SymArrayType(new SymStdType(TYPE::NEXIST), vector<pair<size_t, size_t>>());
            } // end switch
        }
        else{
            vector<pair<string, SymType*>> record_type;
            for (auto &var: type->var_declaration) {
                for (auto &varID: var->ID) {
                    record_type.push_back(make_pair(varID.id, GET_Type_From_Type(var->type, now)));
                }
            }
            return new SymArrayType(new SymRecordType(record_type), std::move(array_range));
        }
    }
    else if (type->mold == "record") {
        vector<pair<string, SymType*>> record_type;
        for (auto &var: type->var_declaration) {
            for (auto &varID: var->ID) {
                record_type.push_back(make_pair(varID.id, GET_Type_From_Type(var->type, now)));
            }
        }
        return new SymRecordType(record_type);
    }
    return nullptr;
}

void Int_To_Real(PTS& type_int, list<Quadruple*>& Q)
{
    string T = concatTheT();
    Quadruple_Add(Q, T, type_int.second, Operator::INTTOREAL, "");
    type_int.first = TYPE::FLOAT;
    type_int.second = T;
}

TYPE TYPE_CHECK(PTS& left, PTS& right, Operator op, list<Quadruple*>& Q)
{
    if(left.first == TYPE::NEXIST || right.first == TYPE::NEXIST){
        return TYPE::NEXIST;
    }
    if(op == Operator::ASS){
        if(left.first == right.first){
            return left.first;
        }
        else if(left.first == TYPE::FLOAT && right.first == TYPE::INT){
            Int_To_Real(right, Q);
            return TYPE::FLOAT;
        }
        else{
            return TYPE::NEXIST;
        }
    }
    else if(op == Operator::EQUAL || op == Operator::NEQUAL || op == Operator::LESS || op == Operator::LE || op == Operator::GREATER || op == Operator::GE){
        if(left.first == right.first){
            return TYPE::BOOL;
        }
        else if(left.first == TYPE::FLOAT && right.first == TYPE::INT){
            Int_To_Real(right, Q);
            return TYPE::BOOL;            
        }
        else if(left.first == TYPE::INT && right.first == TYPE::FLOAT){
            Int_To_Real(left, Q);
            return TYPE::BOOL;                      
        }
        else{
            return TYPE::NEXIST;
        }
    }
    else if(op == Operator::ADD || op == Operator::SUB || op == Operator::MUL || op == Operator::DIV || op == Operator::DIVISION){
        if(left.first != TYPE::INT && left.first != TYPE::FLOAT){
            return TYPE::NEXIST;
        }
        if(right.first != TYPE::INT && right.first != TYPE::FLOAT){
            return TYPE::NEXIST;
        }
        if(left.first == right.first){
            return left.first;
        }
        else if(left.first == TYPE::FLOAT && right.first == TYPE::INT){
            Int_To_Real(right, Q);
            return TYPE::FLOAT;            
        }
        else if(left.first == TYPE::INT && right.first == TYPE::FLOAT){
            Int_To_Real(left, Q);
            return TYPE::FLOAT;                      
        }
        else{
            return TYPE::NEXIST;
        }        
    }
    else if(op == Operator::AND || op == Operator::OR){
        if(left.first != right.first){
            return TYPE::NEXIST;
        }
        if(left.first == TYPE::BOOL){
            return TYPE::BOOL;
        }
        else if(left.first == TYPE::INT){
            return TYPE::INT;
        }
        else if(left.first == TYPE::CHAR){
            return TYPE::CHAR;
        }
        else{
            return TYPE::NEXIST;
        }
    }
    else if(op == Operator::MOD){
        if(left.first != TYPE::INT || right.first != TYPE::INT){
            return TYPE::NEXIST;
        }
        else{
            return TYPE::INT;
        }
    }
    return TYPE::NEXIST;
}

TYPE TYPE_CHECK(PTS& Type, Operator op, list<Quadruple*>& Q)
{
    if(Type.first == TYPE::NEXIST){
        return TYPE::NEXIST;
    }
    if(op == Operator::NOT){
        if(Type.first == TYPE::BOOL){
            return TYPE::BOOL;
        }
        else if(Type.first == TYPE::INT){
            return TYPE::INT;
        }
        else{
            return TYPE::NEXIST;
        }
    }
    return TYPE::NEXIST;
}

void GET_Compound_Statement(const Compound_Statement* compound_statement, SymbolTable* now, list<Quadruple*>& Q)
{
    for(auto it = compound_statement->statement_list.begin(); it != compound_statement->statement_list.end(); it++){
        AUTO_GET_Statement(*it, now, Q);
    }
}

void AUTO_GET_Statement(const Statement* pStatement, SymbolTable *now, list<Quadruple*> &Q)
{
    string str = pStatement->type;
    if(str == "assign"){
        Assign_Statement* assign_statement = (Assign_Statement*) (pStatement);
        GET_Assign_Statement(assign_statement, now, Q);
    }
    else if(str == "procedure"){
        Procedure_Statement* procedure_statement = (Procedure_Statement*) (pStatement);
        GET_Procedure_Statement(procedure_statement, now, Q);
    }
    else if(str == "compound"){
        Compound_Statement* new_compound_statement = (Compound_Statement*) (pStatement);
        GET_Compound_Statement(new_compound_statement, now, Q);
    }
    else if(str == "if"){
        IF_Statement* if_statement = (IF_Statement*) (pStatement);
        GET_IF_Statement(if_statement, now, Q);
    }
    else if(str == "case"){
        CASE_Statement* case_statement = (CASE_Statement*) (pStatement);
        GET_CASE_Statement(case_statement, now, Q);
    }
    else if(str == "while"){
        WHILE_Statement* while_statement = (WHILE_Statement*) (pStatement);
        GET_WHILE_Statement(while_statement, now, Q);
    }
    else if(str == "repeat"){
        REPEAT_Statement* repeat_statement = (REPEAT_Statement*)(pStatement);
        GET_REPEAT_Statement(repeat_statement, now, Q);
    }
    else if(str == "for"){
        FOR_Statement* for_statement = (FOR_Statement*)(pStatement);
        GET_FOR_Statement(for_statement, now, Q);
    }
}

void GET_Assign_Statement(const Assign_Statement* assign_statement, SymbolTable* now, list<Quadruple*>& Q)
{
    bool is_address = false;
    PTS right = GET_Expression(assign_statement->expression, now, Q);
    PTS left = GET_Variable(assign_statement->variable, now, Q, true, is_address);
    TYPE type = TYPE_CHECK(left, right, Operator::ASS, Q);
    if(type != TYPE::NEXIST){
        if(is_address){
            Quadruple_Add(Q, left.second, right.second, Operator::ASTA, "");
        }
        else{
            Quadruple_Add(Q, left.second, right.second, Operator::ASS, "");
        }
    }
}

void GET_Procedure_Statement(const Procedure_Statement* procedure_statement, SymbolTable* now, list<Quadruple*>& Q)
{
    string id = procedure_statement->ID.id;
    vector<TYPE> parameter;
    vector<string> actual_parameter;
    for(auto it = procedure_statement->actual_parameter.begin(); it != procedure_statement->actual_parameter.end(); it++){
        PTS res = GET_Expression((*it), now, Q);
        if(res.first != TYPE::NEXIST){
            parameter.push_back(res.first);
            actual_parameter.push_back(res.second);
        }
    }
    if(id == "read"){
        if(actual_parameter.size() != 1){
            //error handle
            return;
        }
        TYPE in_type = parameter[0];
        string in_name = actual_parameter[0];
        if(in_type == TYPE::CHAR){
            Quadruple_Add(Q, in_name, "", Operator::RC, "");
        }
        else if(in_type == TYPE::INT){
            Quadruple_Add(Q, in_name, "", Operator::RI, "");
        }
        else if(in_type == TYPE::FLOAT){
            Quadruple_Add(Q, in_name, "", Operator::RF, "");
        }
        return;
    }
    else if(id == "write"){
        if(actual_parameter.size() != 1){
            //error handle
            return;
        }
        TYPE in_type = parameter[0];
        string in_name = actual_parameter[0];
        if(in_type == TYPE::CHAR){
            Quadruple_Add(Q, in_name, "", Operator::WC, "");
        }
        else if(in_type == TYPE::INT){
            Quadruple_Add(Q, in_name, "", Operator::WI, "");
        }
        else if(in_type == TYPE::FLOAT){
            Quadruple_Add(Q, in_name, "", Operator::WF, "");
        }
        else if(in_type == TYPE::STRING){
            Quadruple_Add(Q, in_name, "", Operator::WS, "");
        }
        return;
    }
    else if(id == "writeln"){
        if(actual_parameter.size() != 1){
            //error handle
            return;
        }
        TYPE in_type = parameter[0];
        string in_name = actual_parameter[0];
        if(in_type == TYPE::CHAR){
            Quadruple_Add(Q, in_name, "", Operator::WC, "");
            Quadruple_Add(Q, "", "", Operator::NL, "");
        }
        else if(in_type == TYPE::INT){
            Quadruple_Add(Q, in_name, "", Operator::WI, "");
            Quadruple_Add(Q, "", "", Operator::NL, "");
        }
        else if(in_type == TYPE::FLOAT){
            Quadruple_Add(Q, in_name, "", Operator::WF, "");
            Quadruple_Add(Q, "", "", Operator::NL, "");
        }
        else if(in_type == TYPE::STRING){
            Quadruple_Add(Q, in_name, "", Operator::WS, "");
            Quadruple_Add(Q, "", "", Operator::NL, "");
        }
        return;
    }
    bool find_proc = now->find_Procedure(id, parameter);
    if(find_proc){
        vector<bool> parameter_type;
        now->find_all_parameter(id, parameter_type);
        int number = (int) actual_parameter.size();
        for(int i = 0; i < number; i++){
            if(parameter_type[i]){
                Quadruple_Add(Q, actual_parameter[i], "", Operator::REF, "");
            }
            else{
                Quadruple_Add(Q, actual_parameter[i], "", Operator::ARG, "");
            }
        }
        Quadruple_Add(Q, id, "", Operator::CALP, "");
    }
}

void GET_IF_Statement(const IF_Statement* if_statement, SymbolTable *now, list<Quadruple*> &Q)
{
    PTS cond = GET_Expression(if_statement->expression, now, Q);
    // 条件语句四元式的最后一句
    auto cond_last_it = std::prev(Q.end());

    if (if_statement->then_statement != nullptr && if_statement->else_statement != nullptr) {
        AUTO_GET_Statement(if_statement->then_statement, now, Q);
        // then语句四元式的最后一句
        auto then_last_it = std::prev(Q.end());
        // else语句开始的位置，由于需要在条件语句和then语句后插入跳转指令
        auto else_label = nextLabel();
        Q.push_back(new Quadruple("", else_label, Operator::LAB, ""));
        AUTO_GET_Statement(if_statement->else_statement, now, Q);
        // 其他语句开始的位置，由于需要在条件语句和then语句后插入跳转指令，并且是下一条语句
        auto other_label = nextLabel();
        Q.push_back(new Quadruple("", other_label, Operator::LAB, ""));

        // 判断不成功,跳转到else语句的开头
        auto cond_jump = new Quadruple("", cond.second, Operator::JNE, else_label);
        Q.insert(++cond_last_it, std::move(cond_jump));

        // then语句执行结束，跳转到else语句的末尾的下一句，即其他语句的开头
        auto then_jump = new Quadruple("", "", Operator::JMP, other_label);
        Q.insert(++then_last_it, std::move(then_jump));
    }
    else if (if_statement->then_statement != nullptr && if_statement->else_statement == nullptr) {
        AUTO_GET_Statement(if_statement->then_statement, now, Q);
        // 其他语句开始的位置，由于需要在条件语句和then语句后插入跳转指令，并且是下一条语句
        auto other_label = nextLabel();
        Q.push_back(new Quadruple("", other_label, Operator::LAB, ""));
        // 判断不成功,跳转到下一条语句的开头
        auto cond_jump = new Quadruple("", cond.second, Operator::JNE, other_label);
        Q.insert(++cond_last_it, std::move(cond_jump));
    }
    else if (if_statement->then_statement == nullptr && if_statement->else_statement != nullptr) {
        // 错误处理
    }
    else {
        // 错误处理
    }
}

void GET_CASE_Statement(const CASE_Statement* case_statement, SymbolTable* now, list<Quadruple*> &Q)
{

}

// 循环模式：https://www.tutorialspoint.com/pascal/images/pascal_while_do_loop.jpg
void GET_WHILE_Statement(const WHILE_Statement* while_statement, SymbolTable* now, list<Quadruple*> &Q) 
{
    // condition的第一条语句，由于condition还没加入四元式，所以位置是Q.size()
    auto cond_label = nextLabel();
    Q.push_back(new Quadruple("", cond_label, Operator::LAB, ""));
    PTS cond = GET_Expression(while_statement->expression, now, Q);
    auto cond_last_it = std::prev(Q.end());
    AUTO_GET_Statement(while_statement->statement, now, Q);
    // 在循环体末尾加上无条件跳转指令
    auto loop_end_jump = new Quadruple("", "", Operator::JMP, cond_label);
    Q.push_back(std::move(loop_end_jump));
    // 下一条语句的label
    auto other_label = nextLabel();
    Q.push_back(new Quadruple("", other_label, Operator::LAB, ""));

    // 判断不成功，跳转到循环结束位置
    auto cond_false_jump = new Quadruple("", cond.second, Operator::JE, other_label);
    Q.insert(++cond_last_it, std::move(cond_false_jump));

}

// 循环模式：https://www.tutorialspoint.com/pascal/images/pascal_repeat_until_loop.jpg
void GET_REPEAT_Statement(const REPEAT_Statement *repeat_statement, SymbolTable *now, list<Quadruple*> &Q)
{
    // 循环体的第一条语句，由于循环体还没加入四元式，所以位置是Q.size()
    auto start_label = nextLabel();
    Q.push_back(new Quadruple("", start_label, Operator::LAB, ""));
    for (auto &s: repeat_statement->statement) {
        AUTO_GET_Statement(s, now, Q);
    }
    PTS cond = GET_Expression(repeat_statement->expression, now, Q);
    
    auto cond_false_jump = new Quadruple("", cond.second, Operator::JE, start_label);
    Q.push_back(std::move(cond_false_jump));
}

// 循环模式：https://www.tutorialspoint.com/pascal/images/pascal_for_do_loop.jpg
void GET_FOR_Statement(const FOR_Statement *for_statement, SymbolTable *now, list<Quadruple*> &Q)
{
    PTS first_expr = GET_Expression(for_statement->first_expression, now, Q);
    PTS second_expr = GET_Expression(for_statement->second_expression, now, Q);
    PTS left_id = now->find_left_id(for_statement->ID.id);
    if (first_expr.first == TYPE::INT && second_expr.first == TYPE::INT) {
        // assign quadruple;
        Q.push_back(new Quadruple(left_id.second, first_expr.second, Operator::ASS, ""));
        // condition所在的语句
        auto cond_label = nextLabel();
        Q.push_back(new Quadruple("", cond_label, Operator::LAB, ""));
        // 首先需要compare, 比较结果存到temp_var中
        auto temp_var = concatTheT();
        if (for_statement->is_up) {
            Q.push_back(new Quadruple(temp_var, left_id.second, Operator::LE, second_expr.second));
        }
        else {
            Q.push_back(new Quadruple(temp_var, left_id.second, Operator::GE, second_expr.second));
        }
        // condition即将插入的位置
        auto cond_it = std::prev(Q.end());
        // 待执行的statement
        AUTO_GET_Statement(for_statement->statement, now, Q);
        // 常数1
        std::string step = "n_1";
        // increment
        if (for_statement->is_up) { // up
            Q.push_back(new Quadruple(left_id.second, left_id.second, Operator::ADD, step));
        }
        else { // down to
            Q.push_back(new Quadruple(left_id.second, left_id.second, Operator::SUB, step));
        }
        // goto statement, goto到condition位置
        Q.push_back(new Quadruple("", "", Operator::JMP, cond_label));
        // 下一条语句位置
        auto other_label = nextLabel();
        Q.push_back(new Quadruple("", other_label, Operator::LAB, ""));
        // 然后作为condition的跳转条件
        // 当等于0时，跳转退出for
        Q.insert(++cond_it, new Quadruple("", temp_var, Operator::JE, other_label));
    }
    else {
        // 错误处理
    }
} 

PTS GET_Expression(const Expression* expression, SymbolTable* now, list<Quadruple*>& Q)
{
    if(expression->operationType == "relop"){
        PTS left = GET_Simple_Expression(expression->operand_left, now, Q);
        PTS right = GET_Simple_Expression(expression->operand_right, now, Q);
        Operator op;
        if(expression->relop_type == "EQUAL"){
            op = Operator::EQUAL;
        }
        else if(expression->relop_type == "NEQUAL"){
            op = Operator::NEQUAL;
        }
        else if(expression->relop_type == "LESS"){
            op = Operator::LESS;
        }
        else if(expression->relop_type == "LE"){
            op = Operator::LE;
        }
        else if(expression->relop_type == "GREATER"){
            op = Operator::GREATER;
        }
        else if(expression->relop_type == "GE"){
            op = Operator::GE;
        }
        TYPE type = TYPE_CHECK(left, right, op, Q);
        string T = concatTheT();
        if(type != TYPE::NEXIST){
            Quadruple_Add(Q, T, left.second, op, right.second);
            PTS ans = make_pair(type, T);
            return ans;
        }
    }
    else if(expression->operationType == "single"){
        PTS left = GET_Simple_Expression(expression->operand_left, now, Q);
        return left;
    }
    return make_pair(TYPE::NEXIST, "");
}

PTS GET_Simple_Expression(const Simple_Expression* simple_expression, SymbolTable* now, list<Quadruple*>& Q)
{
    if(simple_expression->operationType == "single"){
        PTS right = GET_Term(simple_expression->operand_right, now, Q);
        return right;
    }
    else if(simple_expression->operationType == "ADD"){
        PTS left = make_pair(TYPE::INT, "n_0");
        PTS right = GET_Term(simple_expression->operand_right, now, Q);
        TYPE type = TYPE_CHECK(left, right, Operator::ADD, Q);
        string T = concatTheT();
        if(type != TYPE::NEXIST){
            Quadruple_Add(Q, T, left.second, Operator::ADD, right.second);
            PTS ans = make_pair(type, T);
            return ans;        
        }
    }
    else if(simple_expression->operationType == "SUB"){
        PTS left = make_pair(TYPE::INT, "n_0");
        PTS right = GET_Term(simple_expression->operand_right, now, Q);
        TYPE type = TYPE_CHECK(left, right, Operator::SUB, Q);
        string T = concatTheT();
        if(type != TYPE::NEXIST){
            Quadruple_Add(Q, T, "n_0", Operator::SUB, right.second);
            PTS ans = make_pair(type, T);
            return ans;              
        }
    }
    else if(simple_expression->operationType == "ADDOP"){
        if(simple_expression->ADDOP_type == "ADD"){
            PTS left = GET_Simple_Expression(simple_expression->operand_left, now, Q);
            PTS right = GET_Term(simple_expression->operand_right, now, Q);
            TYPE type = TYPE_CHECK(left, right, Operator::ADD, Q);
            string T = concatTheT();
            if(type != TYPE::NEXIST){
                Quadruple_Add(Q, T, left.second, Operator::ADD, right.second);
                PTS ans = make_pair(type, T);
                return ans;              
            }
        }
        else if(simple_expression->ADDOP_type == "SUB"){
            PTS left = GET_Simple_Expression(simple_expression->operand_left, now, Q);
            PTS right = GET_Term(simple_expression->operand_right, now, Q);
            TYPE type = TYPE_CHECK(left, right, Operator::SUB, Q);
            string T = concatTheT();
            if(type != TYPE::NEXIST){
                Quadruple_Add(Q, T, left.second, Operator::SUB, right.second);
                PTS ans = make_pair(type, T);
                return ans;              
            } 
        }
        else if(simple_expression->ADDOP_type == "OR"){
            PTS left = GET_Simple_Expression(simple_expression->operand_left, now, Q);
            PTS right = GET_Term(simple_expression->operand_right, now, Q);
            TYPE type = TYPE_CHECK(left, right, Operator::OR, Q);
            string T = concatTheT();
            if(type != TYPE::NEXIST){
                Quadruple_Add(Q, T, left.second, Operator::OR, right.second);
                PTS ans = make_pair(type, T);
                return ans;    
            }
        }
    }
    return make_pair(TYPE::NEXIST, "");
}

PTS GET_Term(const Term* term, SymbolTable* now, list<Quadruple*>& Q)
{
    if(term->operationType == "single"){
        PTS right = GET_Factor(term->operand_right, now, Q);
        return right;
    }
    else if(term->operationType == "MULOP"){ 
        
        PTS left = GET_Term(term->operand_left, now, Q);
        PTS right = GET_Factor(term->operand_right, now, Q);
        Operator op;
        if(term->MULOP_type == "MUL"){
            op = Operator::MUL;
        }
        else if(term->MULOP_type == "DIVISION"){
            op = Operator::DIVISION;
        }
        else if(term->MULOP_type == "DIV"){
            op = Operator::DIV;
        }
        else if(term->MULOP_type == "MOD"){
            op = Operator::MOD;
        }
        else if(term->MULOP_type == "AND"){
            op = Operator::AND;
        }
        TYPE type = TYPE_CHECK(left, right, op, Q);
        string T = concatTheT();
        if(type != TYPE::NEXIST){
            Quadruple_Add(Q, T, left.second, op, right.second);
            PTS ans = make_pair(type, T);
            return ans;        
        }
    }
    return make_pair(TYPE::NEXIST, "");
}

PTS GET_Factor(const Factor* factor, SymbolTable* now, list<Quadruple*>& Q)
{
    if(factor->type == 1){
        Unsigned_Const_Variable* un_const_variable = factor->unsigned_const_variable;
        TYPE type = GET_TYPE(un_const_variable->mold);
        string N = "n_";
        if(type == TYPE::BOOL){
            if(un_const_variable->var_value.id == "true"){
                N = N + "1";
            }
            else{
                N = N + "0";
            }
        }
        else if(type == TYPE::CHAR){
            int num = (int) un_const_variable->var_value.id.c_str()[0];
            N = N + to_string(num);
        }
        else if(type == TYPE::FLOAT || type == TYPE::INT){
            N = N + un_const_variable->var_value.id;
        }
        PTS ans = make_pair(type, N);
        return ans;
    }
    else if(factor->type == 2){
        bool is_address = false;
        return GET_Variable(factor->variable, now, Q, false, is_address);
    }
    else if(factor->type == 3){
        string id = factor->ID.id;
        vector<TYPE> parameter;
        vector<string> actual_parameter;
        for(auto it = factor->func_expression.begin(); it != factor->func_expression.end(); it++){
            PTS res = GET_Expression((*it), now, Q);
            if(res.first != TYPE::NEXIST){
                parameter.push_back(res.first);
                actual_parameter.push_back(res.second);
            }
        }
        TYPE Type = now->find_Function(id, parameter);   
        if(Type != TYPE::NEXIST){
            vector<bool> parameter_type;
            now->find_all_parameter(id, parameter_type);
            int number = (int) actual_parameter.size();
            for(int i = 0; i < number; i++){
                if(parameter_type[i]){
                    Quadruple_Add(Q, actual_parameter[i], "", Operator::REF, "");
                }
                else{
                    Quadruple_Add(Q, actual_parameter[i], "", Operator::ARG, "");
                }
            }
            string T = concatTheT();
            Quadruple_Add(Q, T, id, Operator::CALF, "");
            return make_pair(Type, T);
        }
        else{
            return make_pair(TYPE::NEXIST, "");
        }
    }
    else if(factor->type == 4){
        return GET_Expression(factor->expression, now, Q);
    }
    else if(factor->type == 5){
        PTS res = GET_Factor(factor->factor, now, Q);
        if(factor->is_not){
            TYPE type = TYPE_CHECK(res, Operator::NOT, Q);
            string T = concatTheT();
            if(type != TYPE::NEXIST){
                Quadruple_Add(Q, T, res.second, Operator::NOT, "");
                PTS ans = make_pair(type, T);
                return ans;  
            }
        }
        else{
            return res;
        }
    }
    return make_pair(TYPE::NEXIST, "");
}

string GET_Index(Sym_array* array, vector<string>& index_list, list<Quadruple*>& Q)
{
    if(index_list.size() != array->array_range.size()){
        return "";
    }
    string T = concatTheT();
    Quadruple_Add(Q, T, index_list[0], Operator::ASS, "");
    int num = (int) index_list.size();
    for(int i = 1; i < num; i++){
        string n = array->get_n(i);
        Quadruple_Add(Q, T, T, Operator::MUL, n);
        Quadruple_Add(Q, T, T, Operator::ADD, index_list[i]);
    }
    Quadruple_Add(Q, T, T, Operator::MUL, array->get_w());
    Quadruple_Add(Q, T, T, Operator::SUB, array->get_C());
    return T;
}

PTS GET_Variable(const Variable* variable, SymbolTable* now, list<Quadruple*>& Q, bool is_left, bool &is_address)
{
    int type = variable->type;
    string id = variable->ID.id;
    //错误处理
    if((type & 0b100) == 0b100){
        if(!is_left){
            PTS ans = now->find_right_id(id);
            if(ans.first != TYPE::NEXIST){
                return ans;
            }
            else{
                //error handle
                return make_pair(TYPE::NEXIST, "");
            }
        }
        else{
            PTS ans = now->find_left_id(id);
            if(ans.first != TYPE::NEXIST){
                return ans;
            }
            else{
                //error handle
                return make_pair(TYPE::NEXIST, "");
            }
        }
    }
    else{
        CLASS Class = now->find_all_ID(id);
        if(Class == CLASS::ARRAY){
            if((type & 0b001) == 0){
                vector<string> index_list;
                for(auto it1 = variable->array_range.begin(); it1 != variable->array_range.end(); it1++){
                    auto res = (*it1);
                    for(auto it2 = res.begin(); it2 != res.end(); it2++){
                        PTS ans = GET_Expression((*it2), now, Q);
                        if(ans.first != TYPE::INT){
                            //error handle
                            return make_pair(TYPE::NEXIST, "");
                        }
                        else{
                            index_list.push_back(ans.second);
                        }
                    }
                }
                Sym_array* array= now->find_array_ptr(id);
                string index = GET_Index(array, index_list, Q);
                string T = concatTheT();
                PTS Base = now->find_array(id);
                Quadruple_Add(Q, T, Base.second, Operator::ADD, index);
                if(!is_left){
                    string ans = concatTheT();
                    Quadruple_Add(Q, ans, T, Operator::ASFA, "");
                    return make_pair(Base.first, ans);
                }
                else{
                    is_address = true;
                    return make_pair(Base.first, T);
                }
            }
            else{
                return make_pair(TYPE::NEXIST, "");
            }
        }
        else if(Class == CLASS::RECORD){
            if((type & 0b010) == 0){
                vector<string> V = variable->record_ID;
                PTSI res = now->find_record(id, V);
                TYPE Type = res.first.first;
                string base = res.first.second;
                string index = "n_" + to_string(res.second);
                string T = concatTheT();
                Quadruple_Add(Q, T, base, Operator::ADD, index);
                if(!is_left){
                    string ans = concatTheT();
                    Quadruple_Add(Q, ans, T, Operator::ASFA, "");
                    return make_pair(Type, ans);     
                }
                else{
                    is_address = true;
                    return make_pair(Type, T);  
                }
            }
            else{
                return make_pair(TYPE::NEXIST, "");
            }
        }
        else{
            //error handle
            return make_pair(TYPE::NEXIST, "");
        }

    }
}

std::ostream& operator<<(std::ostream& os, const Quadruple& quad)
{
    switch (quad.op)
    {
    case Operator::LAB:
        os << "Label: " << quad.op1;
        break;
    
    case Operator::FUNC:
        os << "Function: " << quad.dest;
        break;
    
    case Operator::PROC:
        os << "Procedure: " << quad.dest;
        break;
    
    case Operator::ASS:
        os << quad.dest << " := " << quad.op1;
        break;
    
    case Operator::ADD:
        os << quad.dest << " := " << quad.op1 << " + " << quad.op2;
        break;
    
    case Operator::SUB:
        os << quad.dest << " := " << quad.op1 << " - " << quad.op2;
        break;
    
    case Operator::MUL:
        os << quad.dest << " := " << quad.op1 << " * " << quad.op2;
        break;
    
    case Operator::DIV:
        os << quad.dest << " := " << quad.op1 << " / " << quad.op2;
        break;
    
    case Operator::MOD:
        os << quad.dest << " := " << quad.op1 << " % " << quad.op2;
        break;
    
    case Operator::DIVISION:
        os << quad.dest << " := " << quad.op1 << " // " << quad.op2;
        break;
    
    case Operator::INTTOREAL:
        os << quad.dest << " := " << " Int to Real " << quad.op1;
        break;
    
    case Operator::NOT:
        os << quad.dest << " := " << " ! " << quad.op1;
        break;
    
    case Operator::ASFA:
        os << quad.dest << " := " << " *" << quad.op1;
        break;
    
    case Operator::ASTA:
        os << " *" << quad.dest << " := " << quad.op1;
        break;
    
    case Operator::EQUAL:
        os << quad.dest << " := " << quad.op1 << " == " << quad.op2;
        break;
    
    case Operator::NEQUAL:
        os << quad.dest << " := " << quad.op1 << " != " << quad.op2;
        break;
    
    case Operator::LESS:
        os << quad.dest << " := " << quad.op1 << " < " << quad.op2;
        break;
    
    case Operator::LE:
        os << quad.dest << " := " << quad.op1 << " <= " << quad.op2;
        break;
    
    case Operator::GREATER:
        os << quad.dest << " := " << quad.op1 << " > " << quad.op2;
        break;
    
    case Operator::GE:
        os << quad.dest << " := " << quad.op1 << " >= " << quad.op2;
        break;
    
    case Operator::AND:
        os << quad.dest << " := " << quad.op1 << " & " << quad.op2;
        break;
    
    case Operator::OR:
        os << quad.dest << " := " << quad.op1 << " | " << quad.op2;
        break;
    
    case Operator::CMP:
        os << quad.dest << " := " << quad.op1 << " ? " << quad.op2;
        break;
    
    case Operator::JG:
        os << "if " << quad.op1 << " > " << 0 << " goto " << quad.op2;
        break;
    
    case Operator::JL:
        os << "if " << quad.op1 << " < " << 0 << " goto " << quad.op2;
        break;
    
    case Operator::JGE:
        os << "if " << quad.op1 << " >= " << 0 << " goto " << quad.op2;
        break;
    
    case Operator::JLE:
        os << "if " << quad.op1 << " <= " << 0 << " goto " << quad.op2;
        break;
    
    case Operator::JE:
        os << "if " << quad.op1 << " == " << 0 << " goto " << quad.op2;
        break;
    
    case Operator::JNE:
        os << "if " << quad.op1 << " != " << 0 << " goto " << quad.op2;
        break;
    
    case Operator::JMP:
        os << "goto " << quad.op2;
        break;
    
    case Operator::CALP:
        os << "call procedure " << quad.dest;
        break;
    
    case Operator::CALF:
        os << quad.dest << " := " << "call function " << quad.op1;
        break;
    
    case Operator::RETURN:
        os << "return " << quad.dest;
        break;

    case Operator::ARG:
        os << "push " << quad.dest;
        break;
    
    case Operator::REF:
        os << "push var " << quad.dest;
        break;

    case Operator::PARAM:
        os << "parameter " << quad.dest;
        break;
    
    case Operator::RI:
        os << "RI " << quad.dest;
        break;
    
    case Operator::RC:
        os << "RC " << quad.dest;
        break;
    
    case Operator::RF:
        os << "RF " << quad.dest;
        break;
    
    case Operator::WS:
        os << "WS " << quad.dest;
        break;
    
    case Operator::WI:
        os << "WI " << quad.dest;
        break;
    
    case Operator::WC:
        os << "WC " << quad.dest;
        break;
    
    case Operator::WF:
        os << "WF " << quad.dest;
        break;
    
    case Operator::NL:
        os << "NL ";
        break;
    case Operator::END:
        os << "END";
        break;
    default:
        os << "Unknown Operator: " << quad.op;
        break;
    }
    return os;
}

